#ifndef MODEL_CALENDAR_HPP
#define MODEL_CALENDAR_HPP

#include "Model/ModelConfig.hpp"
#include "Model/BoostTimeIntervalTraits.hpp"
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <utils/intervals.hpp>
#include <functional>
#include <list>

namespace Model
{

class MODEL_API Calendar
{
public:
	typedef boost::posix_time::ptime Time;
	typedef Time::date_type Date;
	typedef utils::interval_set< Time, BoostTimeIntervalTraits> PeriodSet;
	typedef PeriodSet::interval_type Period;
	typedef boost::posix_time::time_duration Duration;
	typedef PeriodSet::const_iterator PeriodIterator;

	typedef Period value_type;

public:
	const Time& minimumTime() const { return minTime_; }
	const Time& maximumTime() const { return maxTime_; }
	/// \details
	///		After the minimum time is set, the busy periods are recalculated based on the managed rules.
	void setMinimumTime( const Time& newTime);
	/// \details
	///		After the maximum time is set, the busy periods are recalculated based on the managed rules.
	void setMaximumTime( const Time& newTime);
	/// \details
	///		After the minimum and maximum times are set, the busy periods are recalculated based on the managed rules.
	void extendCalendar( const Time& minimumTime, const Time& maximumTime);

	/// \brief It distributes a given duration, from the given start, using the entire available time intervals.
	/// \param start 
	///		the start of the distribution. If this start time is inside a busy time interval, the end of the busy time 
	///		interval is considered as the start of the distribution.
	/// \param duration
	///		the time duration to be distributed through the available busy intervals.
	/// \param out
	///		the result of the interval distribution.
	/// \todo: now, the case of not enough time in the calendar for the given duration is not handled.
	/// \todo: now, the case of the start outside the calendar boundaries is not handled.....((((......
	void distributeDuration( const Time& start, const Duration& duration, PeriodSet& out);

public:
	class Rule
	{
	public:
		virtual void getPeriods( const Time& start, const Time& end, Calendar::PeriodSet& out) = 0;
	};
	typedef std::list< Rule*> RuleCollection;


	/// \details
	///		It also recalculates the busy periods based on the managed rules.
	void addRule( Rule* pNewRule);

	/// \details
	///		It also recalculates the busy periods based on the managed rules.
	/// \warning 
	///		Be aware that removing the rule from the list also means that the removed rule will have no owner. So 
	///		the one that removes the rule should either set another owner of the rule or delete it.
	void removeRule( Rule* pExistingRule);

	/// \brief It gets the rules to be applied to the calendar for getting the busy periods.
	const RuleCollection& rules() const { return rules_; }

public:
	Calendar( const Time& start, const Time& end);
	virtual ~Calendar();

	const PeriodSet& busyPeriods() const { return busyPeriods_; }

private:
	RuleCollection rules_;
	PeriodSet busyPeriods_;
	Time minTime_;
	Time maxTime_;

protected:
	void recalculateRules();
};

} // namespace Model

#endif // MODEL_CALENDAR_HPP
